Skip to content

feat(sdk): loading state — scene image, frosted card, animated mark#64

Open
LEVI-RIVKIN wants to merge 1 commit into
mainfrom
claude/sdk-iframe-skeleton-ywhs0k
Open

feat(sdk): loading state — scene image, frosted card, animated mark#64
LEVI-RIVKIN wants to merge 1 commit into
mainfrom
claude/sdk-iframe-skeleton-ywhs0k

Conversation

@LEVI-RIVKIN

@LEVI-RIVKIN LEVI-RIVKIN commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Replaces the embed loading skeleton with the interview's real chrome, rendered while the iframe loads:

  • The research's scene image — fetched from {host}/interview/{researchId}/scene-image — painted full-bleed behind the card. Applied only after the image actually loads, layered over the app's default surface color (#f5f2f0 light / #15171e dark / custom brand.bg), so a research with no scene (404), a slow network, or a blocked request degrades to a clean solid color.
  • A frosted translucent card (55% panel + backdrop-filter: blur(22px), matching the interview app's card treatment).
  • The animated Perspective mark (30px) centered in the card — the app's LogoSymbol geometry and timing (official colors, side bars swelling deeper than the center, dots easing down, opacity pulses, right side offset half a cycle), driven by CSS keyframes rather than SMIL: dynamically-inserted SMIL is unreliable in Chromium, and as essential loading motion the mark is not disabled by prefers-reduced-motion (an OS-level animation toggle would otherwise leave a static logo that reads as broken).
  • Scene prefetch on intent: new prefetchSceneImage(researchId, host?) export warms the scene image before deferred embeds open — wired to float-bubble hover/focus and to data-perspective-popup / data-perspective-slider trigger elements. Requests are deduped page-wide; widget/fullpage already fetch at mount, in parallel with the iframe.

Why

The previous skeleton reproduced the interview's welcome card, message lines, and input pill — each a moving target that drifted out of sync with the app. This version renders only stable, brand-owned elements: the scene the research actually uses, the card treatment, and the logo animation, all sourced from the app itself.

Layout

The loading state lives in the host page DOM (overlay sibling of the cross-origin iframe), so responsiveness is a CSS container query on the consumer's slot — not a viewport media query. At the app's own 672px breakpoint the card switches between a centered box (desktop) and full-bleed (mobile / slider / popup / float); the vertical inset uses percentage inset on the absolutely-positioned wrapper so it tracks the slot's height.

API

  • LoadingOptions gains optional researchId and host (used to build the scene URL); all five embed types pass them automatically.
  • prefetchSceneImage(researchId, host?) is exported for consumers who want to warm the scene themselves.
  • LoadingOptions.appearance (hideBranding/hideProgress/hideGreeting) is kept as a deprecated, ignored field so existing TypeScript callers keep compiling — no breaking changes.

Testing

  • packages/sdk unit suite: 418 passed (loading tests rewritten: structure, CSS keyframe animation on the LogoSymbol timing with the 0.75s right-side offset and no SMIL nodes, scene URL construction/encoding, prefetch dedup — including float-bubble hover triggering it — color fallbacks, container-query scoping, deprecated appearance accepted-and-ignored).
  • Typecheck, oxlint, prettier: clean.
  • Verified visually in a real browser: scene + centered frosted card + animated mark on desktop, full-bleed dark panel on mobile, solid-color fallback when the scene 404s; the mark verified animating with prefers-reduced-motion: reduce emulated.
  • The scene endpoint's negative path was verified against staging (404 + max-age=60 when no scene is set).

Changeset

@perspective-ai/sdkminor (new loading design; additive LoadingOptions fields and prefetchSceneImage export).

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

📦 Snapshot Release

Published snapshot packages for this PR:

pnpm add @perspective-ai/sdk@1.13.0-pr-64-20260706211041
pnpm add @perspective-ai/sdk-react@1.13.0-pr-64-20260706211041

Or use the npm tag (always points to the latest snapshot from this PR):

pnpm add @perspective-ai/sdk@pr-64
pnpm add @perspective-ai/sdk-react@pr-64

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the SDK’s embed loading skeleton to be a neutral, container-query–responsive overlay that reflects only the iframe’s outer “shape” (boxed on wide slots, full-bleed on narrow slots), with a calmer single-bar shimmer animation and updated default background colors aligned to the interview app’s surface tokens.

Changes:

  • Replaced the detailed, UI-mirroring skeleton DOM with a minimal structure (content/surface + single shimmer bar) and moved layout/animation to a shared injected stylesheet driven by container queries.
  • Updated default light/dark background and shimmer/border translucency values to match the app’s interview surface and reduce visual flash at handoff.
  • Rewrote unit tests to assert the new structure, shared-style injection, container-query usage, and reduced-motion behavior.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
packages/sdk/src/loading.ts Reworks skeleton implementation to a generic, container-query–responsive overlay with a shared injected stylesheet and per-instance CSS variables.
packages/sdk/src/loading.test.ts Updates tests to validate the new DOM structure, stylesheet injection, and animation/scoping behavior.
.changeset/generic-loading-skeleton.md Adds a patch changeset describing the new neutral skeleton behavior and compatibility notes.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/sdk/src/loading.test.ts
Comment thread packages/sdk/src/loading.ts
@LEVI-RIVKIN LEVI-RIVKIN changed the title feat(sdk): neutral, container-query loading skeleton feat(sdk): loading state — scene image, frosted card, animated mark Jul 6, 2026
@LEVI-RIVKIN LEVI-RIVKIN force-pushed the claude/sdk-iframe-skeleton-ywhs0k branch from 1c8f3c9 to 3e4fbcb Compare July 6, 2026 20:14
@LEVI-RIVKIN LEVI-RIVKIN requested a review from Copilot July 6, 2026 20:29

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.

Comment thread packages/sdk/src/loading.ts
Comment thread packages/sdk/src/loading.ts
@LEVI-RIVKIN LEVI-RIVKIN force-pushed the claude/sdk-iframe-skeleton-ywhs0k branch 2 times, most recently from 0361841 to 7d90c82 Compare July 6, 2026 21:09
Replace the embed loading skeleton with the interview's real chrome, rendered
while the iframe loads:

- The research's scene image (from {host}/interview/{researchId}/scene-image)
  paints full-bleed behind the card, applied only after the image actually
  loads and layered over the app's default surface color (#f5f2f0 light,
  #15171e dark, or a custom brand.bg) — a research without a scene (404), a
  slow network, or a blocked request degrades to a clean solid color.
- A frosted translucent card (55% panel + backdrop blur, matching the app's
  card treatment) centers the animated Perspective mark (30px) — the app's
  LogoSymbol geometry and timing, driven by CSS keyframes rather than SMIL:
  dynamically-inserted SMIL is unreliable in Chromium, and as essential
  loading motion the mark is not disabled by prefers-reduced-motion (an OS
  animation toggle would otherwise leave a static logo that reads as broken).
- The loading state lives in the host page DOM (overlay sibling of the
  cross-origin iframe), so responsiveness is a CSS container query on the
  consumer's slot — not a viewport media query. At the app's own 672px
  breakpoint the card switches between a centered box (desktop) and
  full-bleed (mobile / slider / popup / float); the vertical inset uses
  percentage inset on the absolutely-positioned wrapper so it tracks the
  slot's height.
- New prefetchSceneImage(researchId, host?) warms the scene on intent
  signals — wired to float-bubble hover/focus and to data-perspective-popup /
  -slider trigger elements — so deferred embeds open with the scene cached.
  Requests are deduped page-wide; widget/fullpage already fetch at mount, in
  parallel with the iframe.
- LoadingOptions gains researchId/host (all embed types pass them);
  appearance is kept as a deprecated, ignored field for TS compatibility.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016kJptBdnAQ2VfzrE9ob1Wb
@LEVI-RIVKIN LEVI-RIVKIN force-pushed the claude/sdk-iframe-skeleton-ywhs0k branch from 7d90c82 to ab32b62 Compare July 6, 2026 21:10
@LEVI-RIVKIN LEVI-RIVKIN requested a review from vinaypuppal July 6, 2026 21:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants